package com.zb.frame.base.upgrade.common;

import com.zb.frame.base.upgrade.enums.DBType;
import com.zb.frame.base.upgrade.enums.ValueType;
import com.zb.frame.base.upgrade.utils.DBHelper;
import com.zb.frame.base.utils.DBUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.sql.*;
import java.util.*;

@Component
public class RepositoryUpgradeHelper {

    private static final Logger logger = LoggerFactory.getLogger(RepositoryUpgradeHelper.class);

    private static Map<String, String> configMap = new HashMap<String, String>();
    private static Map<String, String> upAfterMap = new HashMap<String, String>();

    private static String dbUrl;
    private static String tableName;
    private static String moduleUrls;

    @Value("${spring.datasource.url}")
    public void setUrl(String url) {
        dbUrl = url;
    }

    @Value("${zb.config.upgrade.config-table-name}")
    public void setTableName(String name) {
        tableName = name;
    }

    @Value("${zb.config.upgrade.module-paths}")
    public void setModuleUrls(String urls) {
        moduleUrls = urls;
    }

    private static DBType dbType;

    public static void init(){
        dbType = DBType.valueOf(DBHelper.getDbType(dbUrl).toUpperCase());
        String[] urls = moduleUrls.split(",");
        List list = Arrays.asList(urls);
        Connection conn = null;
        try {
            conn = DBUtil.getConnection();
            String sqlCheck = "select * from " + tableName + " where 1!=1;";
            if(UpgradeHelper.checkExist(conn,sqlCheck)){
                String sql = "select * from " + tableName + ";";
                Map<String,String> dataMap = getData(conn,sql);
                for (Object obj : list){
                    String module = (String) obj;
                    if(dataMap.containsKey(module)){
                        configMap.put(module,dataMap.get(module));
                    }else{
                        configMap.put(module,"0.0.0");
                    }
                }
            }else{
                newConfigTable(conn,dbType,tableName);
                init();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(null != conn)
                DBUtil.close(conn);
        }
    }

    public static void upgradeRepository(){
        Connection conn = null;
        try {
            conn = DBUtil.getConnection();
            conn.setAutoCommit(false);
            for (Map.Entry<String, String> entry : configMap.entrySet()) {
                doUpgrade(conn, dbType, entry.getKey(), entry.getValue());
            }
            conn.commit();
            refreshVersion(conn);
            printUpgradeInfo(conn);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(null != conn)
                DBUtil.close(conn);
        }
    }

    public static void doUpgrade(Connection conn, DBType type, String packageName, String version) {
        while(true){
            try {
                UpgradeTask task = ("0.0.0".equals(version)) ? (UpgradeTask) Class.forName(packageName + ".UpgradeTask_New").newInstance() : (UpgradeTask) Class.forName(packageName + ".UpgradeTask_" + version.replace('.', '_')).newInstance();
                if(task.doUpgrade(conn, type)){
                    String str = "0.0.0".equals(version) ? "无" : version;
                    logger.info("升级模块【" + packageName + "】 版本从【" + str + "】到【" + task.getNewVersion() + "】");
                    conn.commit();
                }else {
                    conn.rollback();
                    break;
                }
                version = task.getNewVersion();
                upAfterMap.put(packageName, version);
            } catch(ClassNotFoundException ex) {
                break;
            } catch (Exception e) {
                logger.error("Upgrade Error: " + e.getMessage(), e);
                break;
            }
        }
    }

    public static void refreshVersion(Connection conn) throws SQLException {
        PreparedStatement pre = null;
        Map<String,String> message = getData(conn,"select * from " + tableName + ";");
        try {
            for (Map.Entry<String, String> entry : upAfterMap.entrySet()) {
                String module = entry.getKey();
                String version = entry.getValue();
                if(message.containsKey(module)){
                    String sql = "update " + tableName + " set version_num = ? where module_url = ?";
                    pre = conn.prepareStatement(sql);
                    pre.setString(1,version);
                    pre.setString(2,module);
                    pre.executeUpdate();
                }else{
                    String sql = "insert into " + tableName + "(module_url,version_num) values(?,?)";
                    pre = conn.prepareStatement(sql);
                    pre.setString(1,module);
                    pre.setString(2,version);
                    pre.executeUpdate();
                }
            }
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(null != pre){
                pre.close();
            }
        }
    }

    public static void printUpgradeInfo(Connection conn) {
        try {
            String sql = "select * from " + tableName + ";";
            Map<String,String> message = getData(conn,sql);
            for (Map.Entry<String,String> entry : message.entrySet()){
                logger.info("升级模块【" + entry.getKey() + "】最新版本为：【" + entry.getValue() +"】");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Map<String,String> getData(Connection conn, String sql) throws SQLException {
        Map<String,String> data = new HashMap<String,String>();
        PreparedStatement pre = null;
        ResultSet rs = null;
        try {
            pre = conn.prepareStatement(sql);
            rs = pre.executeQuery();
            while(rs.next()){
                data.put(rs.getString(1),rs.getString(2));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(null != rs)
                rs.close();
            if(null != pre)
                pre.close();
        }
        return data;
    }

    public static void newConfigTable(Connection conn, DBType type, String tableName){
        Statement stmt = null;
        try {
            stmt = conn.createStatement();
            String sql = UpgradeHelper.getCreateSQL(type, tableName,
                    new String[] {"module_url","version_num"},
                    new ValueType[] {ValueType.STRING_VARCHAR, ValueType.STRING_VARCHAR},
                    new boolean[] {true, true},
                    null,
                    new Integer[] {60,20}
            );
            stmt.executeUpdate(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(stmt != null){
                try {
                    stmt.close();
                } catch (SQLException e) {}
            }
        }
    }

}
